package org.mashart.pink.m.gps;

import java.util.Timer;
import java.util.TimerTask;
import org.mashart.pink.m.gui.Gui;
import org.mashart.pink.m.ws.WsConnector;
import org.mashart.pink.m.ws.RestfulClient;
import org.mashart.pink.m.ws.RecordStoreConnector;
import org.mashart.pink.m.ws.FileConnector;
import org.mashart.pink.m.ws.FakeWsConnector;

import org.mashart.pink.m.store.Preferences;

/**
 * The main class where half of the magic happens.
 *
 * Initializes GPS device connector and HTTP client, receives GPS data and
 * POST them over HTTP.
 * Runs in a separated thread.
 */
public class Receiver extends TimerTask implements Runnable {

    /**
     * Our main UI class instance.
     * Again, I'd like to see something like GuiListener here
     */
    private Gui gui;

    /**
     * User id
     */
    private String user;

    /* TODO use buffered input with DataManager
     * Reply: done by using NMEAAssembler, current buffer size is 2
    //private DataManager data;
     */

    /**
     * GPS device connector
     */
    private DeviceConnector connector;

    /**
     * HTTP RESTful client
     */
    private WsConnector ws_client;

    /**
     * true - we're running
     */
    private boolean active;

    /**
     * true on termination
     */
    private boolean finished;

    /**
     * GPS data collection and POSTing interval,
     * milliseconds.
     * @see <code>initialize()</code>
     */
    private int interval;

    /**
     * Timer, schedules the receiver to get a new waypoint periodically
     */
    private Timer timer;

    /**
     * Time counter, counts the time elapsed since receiver is activated.
     * It leaps with the same step as the action interval of the receiver.
     */
    private int runTime;

    /**
     * Buffer of received waypoints. Whenever updated, it will decide whether/when to send the waypoint to the web server.
     */
    private WaypointsBuffer wpBuffer;

    /**
     * Constructor. Instantiates BT device connector,
     * HTTP client and NMEA parser
     */
    public Receiver() {
        //data = DataManager.getInstance(100, 20);
//        connector = new BtConnector();
//        connector = new InternalGpsConnector();
//        ws_client = new RestfulClient();
//        ws_client = new FakeWsConnector();
//        ws_client = new RecordStoreConnector();
        ws_client = new FileConnector();
        wpBuffer = new WaypointsBuffer();
    }

    /**
     * Called from <code>Gui</code> class
     */
    public void setGui(Gui gui) {
        this.gui = gui;
    }

    /**
     * Called from <code>Gui</code> class
     */
    public void setWsConnector(WsConnector conn) {
        this.ws_client = conn;
    }

    /**
     * Set GPS device connector
     */
    public void setDeviceConnector(DeviceConnector conn){
        this.connector = conn;
    }

    /**
     * Get the runtime in milliseconds
     */
    public String getRuntime(){
        return Waypoint.timeMStoString(runTime, true);
    }

    /**
     * Prepares GPS connector and HTTP client for initialization
     */
    public void configure() {
        connector.configure();
        ws_client.configure();
    }

    /**
     * Initializes GPS connector and HTTP client.
     * Called right before thread start
     */
    public int initialize() {
        /* initialize GPS device connector */
        if( connector.initialize() < 1 ){
            return -1;
        }
        /* initialize HTTP client */
        if (ws_client.initialize() < 1) {
            gui.debug("Internet not available at the moment. GPS records will be stored. Pinkr will retry to send them in next execution.");

            // switch to RecordStore
            ws_client = new RecordStoreConnector();
            ws_client.configure();
            if( ws_client.initialize() < 1 ){
                return -1;
            }
//            return -1;
        }

        interval = 1000; // update GPS coordinates every 1 sec
        active = false;
        finished = true;

        Preferences pref = Preferences.getInstance();
        user = pref.get("user");
        return 1;
    }

    public int activate(){
        if(active){
            return 0;
        }
        connector.activate();
        active = true;
        timer = new Timer();
        timer.schedule(this, 0, interval);
        return 1;
    }

    public int deactivate(){
        if(!active){
            return 0;
        }
        active = false;
        timer.cancel();
        waitTilFinish();
        connector.deactivate();
        return 1;
    }

    public int startRound(){
        // reset the timer
        runTime = 0;

        StringBuffer msg = new StringBuffer();
        msg.append("id=").append(user).append("&").append("action=start");
        ws_client.push(msg.toString().getBytes());
        return activate();
    }

    public int stopRound(){
        StringBuffer msg = new StringBuffer();
        msg.append("id=").append(user).append("&").append("action=stop");
        ws_client.push(msg.toString().getBytes());
        return deactivate();
    }

    public int resumeRound(){
        StringBuffer msg = new StringBuffer();
        msg.append("id=").append(user).append("&").append("action=resume");
        ws_client.push(msg.toString().getBytes());
        return activate();
    }

    public int pauseRound(){
        StringBuffer msg = new StringBuffer();
        msg.append("id=").append(user).append("&").append("action=pause");
        ws_client.push(msg.toString().getBytes());
        return deactivate();
    }

    /**
     * Should be called from the main <code>Gui</code> class
     */
    public void terminate() {
        // finalized the waypoints buffer, i.e., send the last received waypoint.
//        wpBuffer.finalized(ws_client);
        deactivate();
        waitTilFinish();
        if (connector.terminate() < 1) {
            gui.showErrorDialog("Can't terminate GPS receiver", false);
        }
        if (ws_client.terminate() < 1) {
            gui.showErrorDialog("Can't terminate web service connection", false);
        }
    }

    /**
     * Thread entry point
     */
    public void run() {
//        synchronized(sem){
            finished = false;
            runTime += interval;

            Waypoint wp;
            //gui.setCurrentBufferSize(data.getSize());
            //gui.output(parser.getInfo());
                wp = connector.getWaypoint();
                if (wp != null) {
                    // Display coords on the cell phone
                    gui.debug(wp.toString());
                    gui.setClock( Waypoint.timeMStoString(runTime, true) + '\n' );
                    //TODO send only fixed waypoint
//                    if(wp.fix){

                    // Send it to the waypoints buffer, which will decide whether/when to send the waypoint to the web serivce.
//                        wpBuffer.update(ws_client, wp);

                    //                            // Send it to our web service
//                                                    ws_client.push(wp);
                        StringBuffer msg = new StringBuffer();
                        msg.append("id=").append(user).append("&").append(wp.toHttpQuery());
                        ws_client.push(msg.toString().getBytes());
                        // release the memory of the waypoint
                        wp = null;
//                    }

                }
            finished = true;
//        sem.notify();
//        }
    }

    /**
     * Makes sure we close all open connections/streams
     */
    public void waitTilFinish() {
//        synchronized(sem){
            while(!finished){
                try{
//                    sem.wait();
                    Thread.sleep(interval);
                }catch(InterruptedException e){

                }
            }
//        }
    }
}
